﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


namespace SiruEngine.Graphics
{
	/// <summary>
	/// Base class for simple geometric primitive models. This provides a vertex
	/// buffer, an index buffer, plus methods for drawing the model. Classes for
	/// specific types of primitive (CubePrimitive, SpherePrimitive, etc.) are
	/// derived from this common base, and use the AddVertex and AddIndex methods
	/// to specify their geometry.
	/// </summary>
	public abstract class GeometricPrimitive : IDisposable
	{
		#region Fields

		// During the process of constructing a primitive model, vertex
		// and index data is stored on the CPU in these managed lists.
		List<VertexPositionNormal > vertices = new List<VertexPositionNormal >();
		List<ushort> indices = new List<ushort>();

		// Once all the geometry has been specified, the InitializePrimitive
		// method copies the vertex and index data into these buffers, which
		// store it on the GPU ready for efficient rendering.
		VertexBuffer vertexBuffer;
		IndexBuffer indexBuffer;

		// Primitives type to pass to Effect
        PrimitiveType primitive_type = PrimitiveType.TriangleList;

		#endregion

		#region Initialization


		/// <summary>
		/// Adds a new vertex to the primitive model. This should only be called
		/// during the initialization process, before InitializePrimitive.
		/// </summary>
		protected void AddVertex(Vector3 position, Vector3 normal)
		{
			vertices.Add(new VertexPositionNormal(position, normal));
		}


		/// <summary>
		/// Adds a new index to the primitive model. This should only be called
		/// during the initialization process, before InitializePrimitive.
		/// </summary>
		protected void AddIndex(int index)
		{
			if (index > ushort.MaxValue)
				throw new ArgumentOutOfRangeException("index");

			indices.Add((ushort) index);
		}


		/// <summary>
		/// Queries the index of the current vertex. This starts at
		/// zero, and increments every time AddVertex is called.
		/// </summary>
		protected int CurrentVertex
		{
			get { return vertices.Count; }
		}


		/// <summary>
		/// Once all the geometry has been specified by calling AddVertex and AddIndex,
		/// this method copies the vertex and index data into GPU format buffers, ready
		/// for efficient rendering.
		protected void InitializePrimitive(GraphicsDevice graphicsDevice)
		{
			// Create a vertex buffer, and copy our vertex data into it.
			vertexBuffer = new VertexBuffer(graphicsDevice,
                typeof(VertexPositionNormal), vertices.Count, BufferUsage.None);

			vertexBuffer.SetData(vertices.ToArray());

			// Create an index buffer, and copy our index data into it.
			indexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort),
										  indices.Count, BufferUsage.None);

			indexBuffer.SetData(indices.ToArray());
		}

		protected void ChangePrimitiveType(PrimitiveType type)
		{
            primitive_type = type;
		}


		/// <summary>
		/// Finalizer.
		/// </summary>
		~GeometricPrimitive()
		{
			Dispose(false);
		}


		/// <summary>
		/// Frees resources used by this object.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}


		/// <summary>
		/// Frees resources used by this object.
		/// </summary>
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (vertexBuffer != null)
					vertexBuffer.Dispose();

				if (indexBuffer != null)
					indexBuffer.Dispose();
			}
		}


		#endregion

		#region Draw


		/// <summary>
		/// Draws the primitive model, using the specified effect. Unlike the other
		/// Draw overload where you just specify the world/view/projection matrices
		/// and color, this method does not set any renderstates, so you must make
		/// sure all states are set to sensible values before you call it.
		/// </summary>
        public void Draw(GraphicsDevice graphicsDevice)
		{
            graphicsDevice.SetVertexBuffer(vertexBuffer);
			graphicsDevice.Indices = indexBuffer;

            int primitive_count = indexBuffer.IndexCount / 3;
            if (primitive_type == PrimitiveType.TriangleList ||
                primitive_type == PrimitiveType.TriangleStrip)
                primitive_count = indices.Count / 3;
            else
                primitive_count = indices.Count / 2;

            graphicsDevice.DrawIndexedPrimitives(primitive_type, 0, 0,
                vertexBuffer.VertexCount, 0, primitive_count);

            ////graphicsDevice.DrawUserIndexedPrimitives <VertexPositionNormal>(
            ////        primitive_type,
            ////        vertices, 0, vertexBuffer.VertexCount,
            ////        indices, 0, primitive_count);


            ////// Draw the model, using the specified effect.
            ////effect.Begin();

            ////foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
            ////{
            ////    effectPass.Begin();

            ////    // get primitive count
            ////    int primitiveCount;

            ////    if (prim_type == PrimitiveType.TriangleFan ||
            ////        prim_type == PrimitiveType.TriangleList ||
            ////        prim_type == PrimitiveType.TriangleStrip)
            ////        primitiveCount = indices.Count / 3;
            ////    else if (prim_type == PrimitiveType.PointList)
            ////        primitiveCount = indices.Count;
            ////    else
            ////        primitiveCount = indices.Count / 2;

            ////    graphicsDevice.DrawIndexedPrimitives(prim_type, 0, 0,
            ////                                         vertices.Count, 0, primitiveCount);

            ////    effectPass.End();
            ////}

            ////effect.End();
		}


        /////// <summary>
        /////// Draws the primitive model, using a BasicEffect shader with default
        /////// lighting. Unlike the other Draw overload where you specify a custom
        /////// effect, this method sets important renderstates to sensible values
        /////// for 3D model rendering, so you do not need to set these states before
        /////// you call it.
        /////// </summary>
        ////public void Draw(Matrix world, Matrix view, Matrix projection, Color color)
        ////{
        ////    // Set BasicEffect parameters.
        ////    basicEffect.World = world;
        ////    basicEffect.View = view;
        ////    basicEffect.Projection = projection;
        ////    basicEffect.DiffuseColor = color.ToVector3();
        ////    basicEffect.Alpha = color.A / 255.0f;

        ////    // Set important renderstates.
        ////    RenderState renderState = basicEffect.GraphicsDevice.RenderState;

        ////    renderState.AlphaTestEnable = false;
        ////    renderState.DepthBufferEnable = true;
        ////    renderState.DepthBufferFunction = CompareFunction.LessEqual;

        ////    if (color.A < 255)
        ////    {
        ////        // Set renderstates for alpha blended rendering.
        ////        renderState.AlphaBlendEnable = true;
        ////        renderState.AlphaBlendOperation = BlendFunction.Add;
        ////        renderState.SourceBlend = Blend.SourceAlpha;
        ////        renderState.DestinationBlend = Blend.InverseSourceAlpha;
        ////        renderState.SeparateAlphaBlendEnabled = false;
        ////        renderState.DepthBufferWriteEnable = false;
        ////    }
        ////    else
        ////    {
        ////        // Set renderstates for opaque rendering.
        ////        renderState.AlphaBlendEnable = false;
        ////        renderState.DepthBufferWriteEnable = true;
        ////    }

        ////    // Draw the model, using BasicEffect.
        ////    Draw(basicEffect);
        ////}


		/// <summary>
		/// 픽킹이 됐는지 검사한다. pt_cursor은 projection space (z=1) 내에 마우스 2D 값이다.
		/// </summary>
		public virtual bool Picking(Matrix world, Matrix view, Matrix projection, Vector2 pt_cursor)
		{
			return false;
		}

		#endregion
	}
}
